package com.common.utils.excel;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.sax.AbstractExcelSaxReader;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import cn.hutool.poi.exceptions.POIException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.model.HSSFFormulaParser;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

/**
 * @Author: zengliang
 * @Description: Excel文件解析器(.xls)
 * @Date:Created in 11:45 2022/01/04
 * @Modified By:
 *
 */
public class ExcelSaxReader extends AbstractExcelSaxReader<ExcelSaxReader> implements HSSFListener {
    private boolean isOutputFormulaValues = true;
    private SheetRecordCollectingListener workbookBuildingListener;
    private HSSFWorkbook stubWorkbook;
    private SSTRecord sstRecord;
    private FormatTrackingHSSFListener formatListener;
    private List<BoundSheetRecord> boundSheetRecords = new ArrayList();
    private boolean isOutputNextStringRecord;
    private List<Object> rowCellList = new ArrayList();
    private int rid = -1;
    private int curRid = -1;
    private RowHandler rowHandler;

    public ExcelSaxReader(RowHandler rowHandler) {
        this.rowHandler = rowHandler;
    }

    @Override
    public ExcelSaxReader read(File file, int rid) throws POIException {
        try {
            return this.read(new POIFSFileSystem(file), rid);
        } catch (IOException var4) {
            throw new POIException(var4);
        }
    }

    @Override
    public ExcelSaxReader read(InputStream excelStream, int rid) throws POIException {
        try {
            return this.read(new POIFSFileSystem(excelStream), rid);
        } catch (IOException var4) {
            throw new POIException(var4);
        }
    }

    public ExcelSaxReader read(POIFSFileSystem fs, int rid) throws POIException {
        this.rid = rid;
        this.formatListener = new FormatTrackingHSSFListener(new MissingRecordAwareHSSFListener(this));
        HSSFRequest request = new HSSFRequest();
        if (this.isOutputFormulaValues) {
            request.addListenerForAllRecords(this.formatListener);
        } else {
            this.workbookBuildingListener = new SheetRecordCollectingListener(this.formatListener);
            request.addListenerForAllRecords(this.workbookBuildingListener);
        }

        HSSFEventFactory factory = new HSSFEventFactory();

        try {
            factory.processWorkbookEvents(request, fs);
        } catch (IOException var9) {
            throw new POIException(var9);
        } finally {
            IoUtil.close(fs);
        }

        return this;
    }

    public int getSheetIndex() {
        return this.rid;
    }

    public String getSheetName() {
        return this.boundSheetRecords.size() > this.rid ? ((BoundSheetRecord)this.boundSheetRecords.get(this.rid > -1 ? this.rid : this.curRid)).getSheetname() : null;
    }

    @Override
    public void processRecord(Record record) {
        if (this.rid <= -1 || this.curRid <= this.rid) {
            if (record instanceof BoundSheetRecord) {
                this.boundSheetRecords.add((BoundSheetRecord)record);
            } else if (record instanceof SSTRecord) {
                this.sstRecord = (SSTRecord)record;
            } else if (record instanceof BOFRecord) {
                BOFRecord bofRecord = (BOFRecord)record;
                if (bofRecord.getType() == 16) {
                    if (this.workbookBuildingListener != null && this.stubWorkbook == null) {
                        this.stubWorkbook = this.workbookBuildingListener.getStubHSSFWorkbook();
                    }

                    ++this.curRid;
                }
            } else if (this.isProcessCurrentSheet()) {
                if (record instanceof MissingCellDummyRecord) {
                    MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
                    this.addToRowCellList(mc.getColumn(), "");
                } else if (record instanceof LastCellOfRowDummyRecord) {
                    this.processLastCell((LastCellOfRowDummyRecord)record);
                } else {
                    this.processCellValue(record);
                }
            }

        }
    }

    private void addToRowCellList(int index, Object value) {
        while(index > this.rowCellList.size()) {
            this.rowCellList.add("");
        }

        this.rowCellList.add(index, value);
    }

    private void processCellValue(Record record) {
        Object value = null;
        switch(record.getSid()) {
            case 6:
                FormulaRecord formulaRec = (FormulaRecord)record;
                if (this.isOutputFormulaValues) {
                    if (Double.isNaN(formulaRec.getValue())) {
                        this.isOutputNextStringRecord = true;
                    } else {
                        value = this.formatListener.formatNumberDateCell(formulaRec);
                    }
                } else {
                    value = StrUtil.wrap(HSSFFormulaParser.toFormulaString(this.stubWorkbook, formulaRec.getParsedExpression()), "\"");
                }

                this.addToRowCellList(formulaRec.getColumn(), value);
                break;
            case 253:
                LabelSSTRecord lsrec = (LabelSSTRecord)record;
                if (null != this.sstRecord) {
                    value = this.sstRecord.getString(lsrec.getSSTIndex()).toString();
                }

                this.addToRowCellList(lsrec.getColumn(), ObjectUtil.defaultIfNull(value, ""));
                break;
            case 513:
                this.addToRowCellList(((BlankRecord)record).getColumn(), "");
                break;
            case 515:
                NumberRecord numrec = (NumberRecord)record;
                String formatString = this.formatListener.getFormatString(numrec);
                if (formatString.contains(".")) {
                    value = numrec.getValue();
                } else if (!formatString.contains("/") && !formatString.contains(":")) {
                    double doubleValue = numrec.getValue();
                    long longPart = (long)doubleValue;
                    if ((double)longPart == doubleValue) {
                        value = longPart;
                    } else {
                        value = doubleValue;
                    }
                } else {
                    value = this.formatListener.formatNumberDateCell(numrec);
                }

                this.addToRowCellList(numrec.getColumn(), value);
                break;
            case 516:
                LabelRecord lrec = (LabelRecord)record;
                value = lrec.getValue();
                this.addToRowCellList(lrec.getColumn(), value);
                break;
            case 517:
                BoolErrRecord berec = (BoolErrRecord)record;
                this.addToRowCellList(berec.getColumn(), berec.getBooleanValue());
                break;
            case 519:
                if (this.isOutputNextStringRecord) {
                    this.isOutputNextStringRecord = false;
                }
        }

    }

    private void processLastCell(LastCellOfRowDummyRecord lastCell) {
        this.rowHandler.handle(this.curRid, lastCell.getRow(), this.rowCellList);
        this.rowCellList = new ArrayList(this.rowCellList.size());
    }

    private boolean isProcessCurrentSheet() {
        return this.rid < 0 || this.curRid == this.rid;
    }
}
